package araxy.blog.service.impl;

import araxy.blog.TotalStatus;
import araxy.blog.entity.Article;
import araxy.blog.exception.BlogException;
import araxy.blog.exception.DeleteException;
import araxy.blog.exception.DownloadException;
import araxy.blog.exception.UploadException;
import araxy.blog.mapper.ArticlesMapper;
import araxy.blog.service.ArticleService;
import araxy.blog.utils.DateUtil;
import araxy.blog.utils.JsonUtil;
import araxy.blog.utils.NIOUtil;
import araxy.blog.vo.ArticleInfoVo;
import araxy.blog.vo.ManagementVo;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.util.*;

import static java.nio.file.StandardOpenOption.*;

@Service
public class ArticleServiceImpl implements ArticleService {
    @Resource
    ArticlesMapper articlesMapper;

    @Override
    @Transactional
    public Boolean insertArticle(Article article) {
        Integer cnt = articlesMapper.maxOrderNo();
        article.setOrderNo(cnt + 1);
        return articlesMapper.insert(article) == 1;
    }

    @Override
    @Transactional
    public Boolean updateArticle(Article article) {
        return articlesMapper.updateByPrimaryKey(article) == 1;
    }

    @Override
    @Transactional
    public Object uploadArticle(Object obj, Article article) throws ParseException {
        Article old = articlesMapper.selectByPrimaryKey(article.getId());
        if (old == null) {
            throw new UploadException("该文章不存在！");
        }
        Path path = null;
        Path backup = null;
        Path oldPath = Paths.get(TotalStatus.getStorage().toString() + old.getArticleAddress());
        FileChannel out = null;

        if (obj != null) {

            Calendar calendar = Calendar.getInstance();
            calendar.setTime(DateUtil.parseTime(old.getCreateTime()));
            int year = calendar.get(Calendar.YEAR);
            int month = calendar.get(Calendar.MONTH) + 1;
            path = Paths.get(TotalStatus.getStorage().toString(), "articles", Integer.toString(year), Integer.toString(month), article.getTitle() + "."+ article.getId() + "." + "md" );


            /*
             * 如果标题没有更改 文件名一样
             * 在temp文件夹下备份
             * bak指向临时备份文件
             * 否则bak指向旧文件
             */
            try {
                if (Files.exists(path)) {
                    backup = Paths.get(TotalStatus.getStorage().toString(),"temp", old.getId() + "." + "bak");
                    NIOUtil.copyFileCover(path,backup);
                    Files.delete(path);
                }
                out = FileChannel.open(path,WRITE,CREATE);
            } catch (IOException e) {
                e.getStackTrace();
                throw new UploadException("文件上传失败 3");
            }

            /*
             * 将文件从缓冲区接收到存储路径
             * */
            if (obj instanceof ByteBuffer) {
                ByteBuffer tmp = (ByteBuffer) obj;
                try {
                    out.write(tmp,0);
                    out.close();
                    System.out.println(Files.exists(path));
                } catch (IOException e) {
                    if (backup != null) {
                        try {
                            NIOUtil.copyFileCover(backup,path);
                        } catch (IOException ioException) {
                            ioException.printStackTrace();
                        }
                    }
                    throw new UploadException("文件上传失败 4");
                }
            } else if (obj instanceof Path) {
                Path tmp = (Path) obj;
                try {
                    FileChannel t = FileChannel.open(tmp,READ,DELETE_ON_CLOSE);
                    t.transferTo(0,t.size(),out);
                    t.close();
                } catch (IOException e) {
                    if (backup != null) {
                        try {
                            NIOUtil.copyFileCover(backup,path);
                        } catch (IOException ioException) {
                            ioException.printStackTrace();
                        }
                    }
                    throw new UploadException("文件上传失败 5");
                }
            } else {
                throw new UploadException("文件上传失败 6");
            }

        }

        /*
        * 将更新后的文件及其相关信息 写入数据库
        * */
        article.setEditTime(DateUtil.getSysTime());
        article.setEditFlag(true);
        if (obj != null) {
            article.setArticleAddress(path.toString().substring(TotalStatus.getStorage().toString().length()));
        }

        if (updateArticle(article)) {
            if (obj != null) {
                //成功更新数据库 关闭FileChannel 删除旧文件
                try {
                    if (backup == null) {
                        Files.delete(oldPath);
                    } else {
                        Files.delete(backup);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return JsonUtil.FlgTrueJSONString;
        } else {
            if (obj != null) {
                //更新数据库失败 将备份的文件还原
                try {
                    Files.delete(path);
                    if (backup != null) {
                        NIOUtil.copyFileCover(backup,path);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            throw new UploadException("数据库更新失败");
        }
    }

    @Override
    @Transactional
    public Boolean deleteArticleById(String id) {
        return articlesMapper.deleteByPrimaryKey(id) == 1;
    }

    @Override
    public Object deleteArticleDataById(String id) {
        Article article = getArticleById(id);
        Path path = Paths.get( TotalStatus.getStorage().toString() + article.getArticleAddress() );
        if (deleteArticleById(id)) {
            try {
                Files.delete(path);
            } catch (IOException e) {
                throw new DeleteException("删除文件失败");
            }
        } else {
            throw new DeleteException("数据库更新失败");
        }
        return JsonUtil.FlgTrueJSONString;
    }

    @Override
    public Article getArticleById(String id) {
        return articlesMapper.selectByPrimaryKey(id);
    }

    @Override
    public Map<String,Object> getAllPagedArticle(Integer page, Integer pageSize) {
        Page<List<ManagementVo>> p = PageHelper.startPage(page,pageSize).setOrderBy("order_no DESC");
        List<ManagementVo> managementVoList = articlesMapper.selectAllManagementVo();
        if (managementVoList.size() < 1) {
            throw new BlogException("暂无文章");
        }
        Map<String,Object> map = new HashMap<>();
        map.put("flg",true);
        map.put("total",p.getTotal());
        map.put("tableData",managementVoList);
        return map;
    }

    @Override
    public Map<String, Object> getPagedArticleInfo(Integer page, Integer pageSize) {
        Page<List<ArticleInfoVo>> p = PageHelper.startPage(page,pageSize).setOrderBy("order_no DESC");
        List<ArticleInfoVo> articleInfoVoList = articlesMapper.selectAllInfoVo();
        if (articleInfoVoList.size() < 1) {
            throw new BlogException("暂无文章");
        }
        Map<String,Object> map = new HashMap<>();
        map.put("flg",true);
        map.put("total",p.getTotal());
        map.put("infoData",articleInfoVoList);
        return map;
    }

    @Override
    @Transactional
    public Object uploadArticle(Path path, Object obj, Article article) {
        if (!Files.exists(path)) {
            try {
                Files.createDirectories(path);
            } catch (IOException e) {
                throw new UploadException("创建路径出错");
            }
        }
        //保存文件
        String fileName = article.getTitle() + "." + article.getId();
        Path newFile;
        FileChannel in;
        FileChannel out;

        try {
            newFile = Paths.get(path.toString(),fileName + "." + "md");
            out = FileChannel.open(newFile,WRITE,CREATE_NEW);
        } catch (IOException e) {
            throw new UploadException("文件上传失败 3");
        }

        if (obj instanceof Path) {
            Path tmp = (Path) obj;
            try {
                in = FileChannel.open(tmp,READ,DELETE_ON_CLOSE);
                in.transferTo(0,in.size(),out);
                in.close();
                out.close();
            } catch (IOException e) {
                try {
                    if (Files.exists(newFile)) {
                        Files.delete(newFile);
                    }
                    throw new UploadException("文件上传失败 4");
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        } else if (obj instanceof ByteBuffer) {
            ByteBuffer tmp = (ByteBuffer) obj;
            try {
                out.write(tmp,0);
                out.close();
            } catch (IOException e) {
                try {
                    if (Files.exists(newFile)) {
                        Files.delete(newFile);
                    }
                    throw new UploadException("文件上传失败 5");
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        } else {
            throw new UploadException("文件上传失败 6");
        }
        //将文件信息写入数据库
        article.setArticleAddress(newFile.toString().substring(TotalStatus.getStorage().toString().length()));
        article.setCreateTime(DateUtil.getSysTime());
        article.setEditFlag(false);
        if (!insertArticle(article)) {
            throw new UploadException("数据库写入失败");
        }
        return JsonUtil.FlgTrueJSONString;
    }

    @Override
    public byte[] getArticleBytesById(String id) {
        Article article = getArticleById(id);
        Path path = Paths.get(TotalStatus.getStorage().toString() + article.getArticleAddress());
        FileChannel fc;
        ByteBuffer buf;
        try {
            fc = FileChannel.open(path,READ);
            int size = Math.toIntExact(fc.size());
            buf = ByteBuffer.allocate(size);
            fc.read(buf);
            fc.close();
        } catch (IOException e) {
            throw new DownloadException("获取文件失败！");
        }
        return buf.array();
    }
}
